/* generate the C file corresponding to the output of the inference */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "libblogc/list.h"
#include "hashtable.h"
#include "node.h"
#include "symbol.h"
#include "global.h"

#include "generate.h"

typedef struct genctx
{
  FILE * fp_genctx;
  list_t obslist_genctx;
  list_t qrylist_genctx;

  /* context used in gennode to determine how to generate a CPD */
  symbol * desttype;
  char * destvar;
} genctx;

typedef struct genqry
{
  struct node * tree;
  int qnum;
  char * reptype;
} genqry;

static void gen_hdrs(genctx * ctx);
static void gen_types(genctx * ctx);
static void gen_remove_add(genctx * ctx);
static void gen_types_init(genctx * ctx);
static void gen_world_type(genctx * ctx);
static void gen_world_init(genctx * ctx);
static void gen_logprob(genctx * ctx);
static void gen_sample(genctx * ctx);
static void gen_birth_death(genctx * ctx);
static void gen_post_sample(genctx * ctx);
static void gen_query(genctx * ctx);
static void gen_main(genctx * ctx);
static void gen_node(genctx * ctx, node * node1);
static void gen_null_conds(genctx * ctx, node * node1, list_t nullconds);

void generate(list_t obslist, list_t qrylist, FILE * fp)
{
  genctx * ctx;

  /* allocate and initialize context */
  ctx = (genctx *)malloc(sizeof(*ctx));
  ctx->fp_genctx = fp;
  
  ctx->obslist_genctx = obslist;
  ctx->qrylist_genctx = qrylist;
  
  gen_hdrs(ctx);
  gen_types(ctx);
  gen_world_type(ctx);
  gen_sample(ctx);
  gen_remove_add(ctx);
  gen_logprob(ctx);
  gen_types_init(ctx);
  gen_world_init(ctx);
  gen_birth_death(ctx);
  gen_post_sample(ctx);
  gen_query(ctx);
  gen_main(ctx);
}

static void gen_hdrs(genctx * ctx)
{
  FILE * fp;

  fp = ctx->fp_genctx;

  fprintf(fp, "#include <stdio.h>\n");
  fprintf(fp, "#include <stdlib.h>\n");
  fprintf(fp, "#include <math.h>\n");
  fprintf(fp, "#include <time.h>\n");
  fprintf(fp, "#include <assert.h>\n");
  fprintf(fp, "#include <blogc/options.h>\n");
  fprintf(fp, "#include <blogc/list.h>\n");
  fprintf(fp, "#include <blogc/rkvector.h>\n");
  fprintf(fp, "#include <blogc/report.h>\n");
  fprintf(fp, "#include <blogc/random.h>\n");
  fprintf(fp, "#include <blogc/poisson.h>\n");
  fprintf(fp, "#include <blogc/gaussian.h>\n");
  fprintf(fp, "#include <blogc/uniform.h>\n");
  fprintf(fp, "#include <blogc/tabular.h>\n");
  fprintf(fp, "#include <blogc/bernoulli.h>\n");
  fprintf(fp, "\n");
}

static void gen_types(genctx * ctx)
{
  symbol * sym;
  list_t iter;
  list_t iter2;
  FILE * fp;

  fp = ctx->fp_genctx;

  /* first generated enumerated types */
  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) && symbol_has_fixed_objects(sym))
    {
      fprintf(fp, "#define NUM_%s %d\n\n",
              sym->name, list_count(sym->t.objlist));
    }
  }
  hashtable_itr_end(iter);
  
  /* next generate structure types */
  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) && sym->t.genstruct)
    {
      fprintf(fp, "/* %s (BLOG type) */\ntypedef struct %s\n{\n", 
              sym->name, sym->genname);

      for (iter2 = list_next(sym->t.attrs); iter2; 
           iter2 = list_next(iter2))
      {
        symbol * fn;
        symbol * rettype;
        char * fmt;
      
        fn = (symbol *)list_item(iter2);
        rettype = fn->f.rettype;
      
        if (!rettype->t.genstruct)
          fmt = "  %s %s;\n";
        else
          fmt = "  struct %s * %s;\n";
          
        fprintf(fp, fmt, rettype->genname, fn->genname);
      }

      for (iter2 = list_next(sym->t.incoming_fns); iter2; 
           iter2 = list_next(iter2))
      {
        symbol * fn;
      
        fn = (symbol *)list_item(iter2);

        fprintf(fp, "  list_t in_%s;\n", fn->genname);
      }

      if (list_is_empty(sym->t.attrs) && list_is_empty(sym->t.incoming_fns))
        fprintf(fp, "  int dummy;\n");
      
      fprintf(fp, "} %s;\n\n", sym->genname);

      if (!list_is_empty(sym->t.objlist))
      {
        fprintf(fp, "#define NUM_%s %d\n\n", sym->name, 
                list_count(sym->t.objlist));
      }
    }
  }
  hashtable_itr_end(iter);
}

static void gen_types_init(genctx * ctx)
{
  symbol * sym;
  list_t iter;
  list_t iter2;
  FILE * fp;

  fp = ctx->fp_genctx;

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) && sym->t.genstruct)
    {
      fprintf(fp, "static void %s_alloc_list(world_t * world, %s obj)\n{\n", 
              sym->name, sym->t.gendeclname);

      fprintf(fp, "  /* initialize the list of incoming pointers */\n");
      for (iter2 = list_next(sym->t.incoming_fns); iter2; 
           iter2 = list_next(iter2))
      {
        symbol * fn;
      
        fn = (symbol *)list_item(iter2);

        fprintf(fp, "  obj->in_%s = list_alloc();\n", fn->genname);
      }
      fprintf(fp, "\n");

      fprintf(fp, "}\n\n");

      fprintf(fp, "static void %s_init_attrs(world_t * world, %s obj)\n{\n", 
              sym->name, sym->t.gendeclname);
      
      fprintf(fp, "  /* initialize the attributes of this object */\n");
      for (iter2 = list_next(sym->t.attrs); iter2; iter2=list_next(iter2))
      {
        symbol * attrsym;
        attrsym = list_item(iter2);
        
        /* normally, we don't need to init origin functions since the
         * birth moves handle this but if the type has fixed objects there
         * are no birth moves so we need to handle it here */
        if (strncmp("num_", attrsym->name, 4)
            && ((SYMBOL_FN_CAT_ORIGIN != attrsym->f.category)
                || symbol_has_fixed_objects(sym)))
        {
          if (attrsym->f.rettype->t.has_guarobjs)
            fprintf(fp, "  obj->%s = world->All_%s+0;"
                    " /* initialize to %s */\n", 
                    attrsym->genname, attrsym->f.rettype->name,
                    ((symbol *) list_item(list_next(attrsym->f.rettype
                                                    ->t.objlist)))->name);

          if (!attrsym->f.rettype->builtin)
            fprintf(fp, "  add_%s(world, obj);\n", attrsym->name);
        }
      }
      fprintf(fp, "\n");

      fprintf(fp, "}\n\n");

      if (symbol_has_unknown_num_objects(sym))
      {
        fprintf(fp, "static %s * %s_alloc(world_t * world)\n{\n", 
                sym->genname, sym->name);
        fprintf(fp, "  %s * obj;\n\n", sym->genname);
        fprintf(fp, "  /* allocate the object */\n");
        fprintf(fp, "  obj = (%s *)calloc(1, sizeof(%s));\n\n", sym->genname,
                sym->genname);
        fprintf(fp, "  %s_alloc_list(world, obj);\n", sym->name);
        fprintf(fp, "  %s_init_attrs(world, obj);\n", sym->name);
        fprintf(fp, "  return obj;\n");
        fprintf(fp, "}\n\n");

        fprintf(fp, "static void %s_free(world_t * world, %s * obj)\n{\n", 
                sym->name, sym->genname);

        /* there better not be any incoming references to the object when
         * it is being freed */
        for (iter2 = list_next(sym->t.incoming_fns); iter2; 
             iter2 = list_next(iter2))
        {
          symbol * fn;
      
          fn = (symbol *)list_item(iter2);

          fprintf(fp, "  assert(list_is_empty(obj->in_%s));\n", fn->genname);
          fprintf(fp, "  list_free(obj->in_%s);\n", fn->genname);
        }

        /* if we refer to any other object then we should remove the
         * corresponding incoming reference from the other object */
        for (iter2 = list_next(sym->t.attrs); iter2; 
             iter2 = list_next(iter2))
        {
          symbol * fn;
      
          fn = (symbol *)list_item(iter2);
          /* we don't need to uninit the origin functions, the death moves
           * handle this */
          if (!fn->f.rettype->builtin 
              && (SYMBOL_FN_CAT_ORIGIN != fn->f.category))
            fprintf(fp, "  remove_%s(world, obj);\n", fn->name);
        }

        fprintf(fp, "  free(obj);\n");
        fprintf(fp, "}\n\n");
      }
    }
  }
  hashtable_itr_end(iter);
}

static void gen_world_type(genctx * ctx)
{
  symbol * sym;
  list_t iter;
  list_t newqrylist;
  int qnum;
  
  FILE * fp;

  fp = ctx->fp_genctx;
  fprintf(fp, "typedef struct world_t\n");
  fprintf(fp, "{\n");
  fprintf(fp, "  int burnin;  /* world is in burn-in phase */\n");

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    /* zero-ary functions have to be declared as a variable in the model */
    if ((SYMBOL_B_FUNCTION == sym->symtype) 
        && ((SYMBOL_FN_CAT_RANDOM == sym->f.category)
            || (SYMBOL_FN_CAT_NUMBER == sym->f.category))
        && list_is_empty(sym->f.argtype_list))
    {
      fprintf(fp, "  %s %s;\n", sym->f.rettype->t.gendeclname, sym->genname);
    }
    else if ((SYMBOL_B_TYPE == sym->symtype) && sym->t.genstruct)
    {
      if (symbol_has_unknown_num_objects(sym))
      {
        fprintf(fp, "  list_t All_%s;\n", sym->name);
        if (sym->t.null_numfn)
          fprintf(fp, "  list_t null_%s;\n", sym->name);
      }
      else
        fprintf(fp, "  %s All_%s[NUM_%s];\n", sym->genname, sym->name,
                sym->name);
    }
  }
  hashtable_itr_end(iter);

  fprintf(fp, "\n  /* CPDs */\n");
  for (iter = list_next(global->distlist); iter; iter = list_next(iter))
  {
    node * cpdnode;
    node * distnode;
    node * parnode;
    node * argnode;
    
    cpdnode = list_item(iter);

    list_item3(cpdnode->childlist, (void **)&distnode, (void **)&parnode,
               (void **)&argnode);
    
    fprintf(fp, "  %s_t %s;\n", distnode->leaf.pchar, cpdnode->genname);
  }
  
  fprintf(fp, "\n  /* variables for queries */\n");

  newqrylist = list_alloc();
  qnum = 1;
  
  for (iter = list_next(ctx->qrylist_genctx); iter; iter = list_next(iter))
  {
    node * qrynode;
    genqry * qry;
    
    qry = (genqry *)malloc(sizeof(*qry));
    
    qrynode = list_item(iter);
    
    qry->tree = qrynode;
    qry->qnum = qnum;
    qnum ++;
    
    if (qrynode->reqtype && !strcmp(qrynode->reqtype->name, "Real"))
    {
      qry->reptype = "Real";
    }
    else if (qrynode->reqtype && !strcmp(qrynode->reqtype->name, "NaturalNum"))
    {
      qry->reptype = "NaturalNum";
    }
    else if (qrynode->reqtype)
    {
      node_error(qrynode, "query rquested type of '%s' not allowed",
                 qrynode->reqtype->name);
    }
    else if (!strcmp(qrynode->valtype->name, "Real"))
    {
      qry->reptype = "Real";
    }
    else if (!strcmp(qrynode->valtype->name, "NaturalNum"))
    {
      if (global->num2real)
        qry->reptype = "Real";
      else
        qry->reptype = "NaturalNum";
    }
    else if (qrynode->valtype->t.has_guarobjs)
    {
      qry->reptype = "Finite";
    }
    else
    {
      node_error(qrynode, "query type of '%s' not allowed",
                 qrynode->valtype->name);
    }

    list_insert_end(newqrylist, qry);

    fprintf(fp, "  Report_%s_t query%d;\n", qry->reptype, qry->qnum);
  }

  ctx->qrylist_genctx = newqrylist;

  fprintf(fp, "  /* a weight and pointer array to aid sampling */\n");
  fprintf(fp, "  double wtarr[10000];\n");
  fprintf(fp, "  void * ptrarr[10000];\n");
  fprintf(fp, "  double dblarr[10000];\n");
  fprintf(fp, "  int intarr[10000];\n");
  
  fprintf(fp, "} world_t;\n\n");
}

static void gen_world_init(genctx * ctx)
{
  symbol * sym;
  list_t iter;
  
  FILE * fp;

  fp = ctx->fp_genctx;

  fprintf(fp, "static world_t * world_alloc(void)\n");
  fprintf(fp, "{\n");
  fprintf(fp, "  world_t * world;\n");
  fprintf(fp, "  int       objnum;\n");
  fprintf(fp, "\n");
  fprintf(fp, "  world = (world_t *)calloc(1, sizeof(world_t));\n");

  fprintf(fp, "\n  /* initialize CPDs */\n");
  for (iter = list_next(global->distlist); iter; iter = list_next(iter))
  {
    node * cpdnode;
    node * distnode;
    node * parnode;
    node * argnode;
    list_t iter2;
    
    cpdnode = list_item(iter);

    list_item3(cpdnode->childlist, (void **)&distnode, (void **)&parnode,
               (void **)&argnode);
    
    fprintf(fp, "  %s_Init_Params(&world->%s, %d", distnode->leaf.pchar,
            cpdnode->genname, list_count(parnode->childlist));

    for (iter2 = list_next(parnode->childlist); iter2;
         iter2 = list_next(iter2))
    {
      fprintf(fp, ", ");
      gen_node(ctx, (node *)list_item(iter2));
    }
    
    fprintf(fp, ");\n");

    fprintf(fp, "  %s_Init_Args(&world->%s, %d", distnode->leaf.pchar,
            cpdnode->genname, list_count(argnode->childlist));

    for (iter2 = list_next(argnode->childlist); iter2;
         iter2 = list_next(iter2))
    {
      node * argnode2;

      argnode2 = list_item(iter2);
      
      fprintf(fp, ", ");

      if (!argnode2->valtype || list_is_empty(argnode2->valtype->t.objlist))
        fprintf(fp, "-1");
      else
        fprintf(fp, "NUM_%s", argnode2->valtype->name);
    }

    fprintf(fp, ");\n");
    
  }

  fprintf(fp, "\n");

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) && (sym->t.genstruct))
    {
      fprintf(fp, "  /* allocate objects of type %s */\n", sym->name);
      if (symbol_has_unknown_num_objects(sym))
      {
        fprintf(fp, "  world->All_%s = list_alloc();\n", sym->name);
        if (sym->t.null_numfn)
        {
          fprintf(fp, "  world->null_%s = list_alloc();\n", sym->name);
        }
      }
      else
      {
        fprintf(fp, "  for(objnum=0; objnum < NUM_%s; objnum++)\n  {\n",
                sym->name);
        fprintf(fp, "    %s_alloc_list(world, world->All_%s + objnum);\n",
                sym->name, sym->name);
        fprintf(fp, "  }\n");
      }
    }
  }
  hashtable_itr_end(iter);
  
  fprintf(fp, "\n");
  
  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) && (sym->t.genstruct))
    {
      if (symbol_has_fixed_objects(sym))
      {
        list_t iter2;

        fprintf(fp, "  /* initialize objects of type %s */\n", sym->name);
        fprintf(fp, "  for(objnum=0; objnum < NUM_%s; objnum++)\n  {\n",
                sym->name);
        fprintf(fp, "    %s_init_attrs(world, world->All_%s + objnum);\n",
                sym->name, sym->name);
        fprintf(fp, "  }\n");


        for (iter2 = list_next(sym->t.objlist); iter2;
             iter2 = list_next(iter2))
        {
          list_t iter3;
          symbol * objsym;
          objsym = list_item(iter2);

          for (iter3 = list_next(objsym->o.obs_attr_vals); iter3; 
               iter3 = list_next(iter3))
          {
            symbol * attrsym;
            node * valnode;
        
            list_item2(list_item(iter3), (void **)&attrsym, (void **)&valnode);

            if (!attrsym->f.rettype->builtin)
              fprintf(fp, "  remove_%s(world, world->All_%s + %d);\n",
                      attrsym->name, sym->name, objsym->o.objnum);
            
            fprintf(fp, "  world->All_%s[%d].%s = ", sym->name, 
                    objsym->o.objnum, attrsym->genname);
            gen_node(ctx, valnode);
            fprintf(fp, "; /* observation */\n");

            if (!attrsym->f.rettype->builtin)
              fprintf(fp, "  add_%s(world, world->All_%s + %d);\n",
                      attrsym->name, sym->name, objsym->o.objnum);
          }
        }
      }
      fprintf(fp, "\n");
    }

    /* TODO * z-ary function can also refer to types with incoming lists */

    else if ((SYMBOL_B_FUNCTION == sym->symtype) && sym->f.zary_obs)
    {
      fprintf(fp, "  world->%s = ", sym->genname);
      gen_node(ctx, sym->f.zary_obs);
      fprintf(fp, "; /* observation */\n\n");
    }
    else if ((SYMBOL_B_FUNCTION == sym->symtype)
             && list_is_empty(sym->f.argtype_list)
             && sym->f.rettype->t.has_guarobjs)
    {
      fprintf(fp, "  /* initialize %s to %s */\n", sym->name,
              ((symbol *) list_item(list_next(sym->f.rettype->t.objlist)))
              ->name);

      fprintf(fp, "  world->%s = world->All_%s + 0;\n\n", sym->genname,
              sym->f.rettype->name);
    }
  }
  hashtable_itr_end(iter);
  

  fprintf(fp, "\n  /* initialize queries */\n");
  
  for (iter = list_next(ctx->qrylist_genctx); iter; iter = list_next(iter))
  {
    genqry * qry;

    qry = list_item(iter);
    fprintf(fp, "  Report_%s_Init(&world->query%d",
            qry->reptype, qry->qnum);

    /* for finite types we need to list out all the guranteed objects */
    if (!strcmp(qry->reptype, "Finite"))
    {
      list_t iter2;
      
      fprintf(fp, ", NUM_%s", qry->tree->valtype->name);
      for (iter2 = list_next(qry->tree->valtype->t.objlist); iter2;
           iter2 = list_next(iter2))
      {
        symbol * objsym;
        
        objsym = list_item(iter2);
        fprintf(fp, ", \"%s\"", objsym->name);
      }
    }

    fprintf(fp, ");\n");
  }
  
  fprintf(fp, "\n  return world;\n");
  fprintf(fp, "}\n\n");
}

static void gen_remove_add(genctx * ctx)
{
  FILE * fp;
  symbol * sym;
  list_t iter;
  char * orig_buf1;
  char * orig_buf2;
  char * buf1;
  char * buf2;

  fp = ctx->fp_genctx;

  orig_buf1 = (char *)malloc(sizeof(char) * 10000);
  orig_buf2 = (char *)malloc(sizeof(char) * 10000);
  
  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_FUNCTION == sym->symtype)
        && ((SYMBOL_FN_CAT_RANDOM == sym->f.category)
            || (SYMBOL_FN_CAT_ORIGIN == sym->f.category))
        && !sym->f.rettype->builtin)
    {
      symbol * typesym;

      buf1 = orig_buf1;
      buf2 = orig_buf2;
      
      if (list_is_empty(sym->f.argtype_list))
      {
        typesym = NULL;
      }
      else
      {
        list_item1(sym->f.argtype_list, (void **)&typesym);
      }

      buf1 += sprintf(buf1, "/* add %s to lists or counts it belongs to"
                      " */\n", sym->name);
      buf2 += sprintf(buf2, "/* remove %s from lists or counts it belongs to "
                      "*/\n", sym->name);

      if (typesym)
      {
        buf1 += sprintf(buf1, "static void add_%s(world_t * world, %s obj)\n",
                        sym->name, typesym->t.gendeclname);
        buf2 += sprintf(buf2, "static void remove_%s(world_t * world, %s obj)"
                        "\n", sym->name, typesym->t.gendeclname);
      }
      else
      {
        buf1 += sprintf(buf1, "static void add_%s(world_t * world)\n",
                        sym->name);
        buf2 += sprintf(buf2, "static void remove_%s(world_t * world)\n",
                        sym->name);
      }
      buf1 += sprintf(buf1, "{\n");
      buf2 += sprintf(buf2, "{\n");


      if (typesym)
      {
        buf1 += sprintf(buf1, "  if (obj->%s)\n  {\n", sym->genname);
        buf1 += sprintf(buf1, "    list_insert_front(obj->%s->in_%s, obj);\n",
                       sym->genname, sym->genname);

        buf2 += sprintf(buf2, "  if (obj->%s)\n  {\n", sym->genname);
        buf2 += sprintf(buf2, "    list_remove(obj->%s->in_%s, obj);\n",
                       sym->genname, sym->genname);
        if (SYMBOL_FN_CAT_ORIGIN == sym->f.category)
        {
          buf1 += sprintf(buf1, "    obj->%s->%s ++;\n",
                          sym->genname, sym->f.numfn->genname);
          buf1 += sprintf(buf1, "  }\n");

          buf2 += sprintf(buf2, "    obj->%s->%s --;\n",
                          sym->genname, sym->f.numfn->genname);
          buf2 += sprintf(buf2, "  }\n");

          if (typesym->t.null_numfn)
          {
            buf1 += sprintf(buf1, "  else\n  {\n    world->%s ++;\n",
                            typesym->t.null_numfn->genname);

            buf2 += sprintf(buf2, "  else\n  {\n    world->%s --;\n",
                            typesym->t.null_numfn->genname);

            if (symbol_has_unknown_num_objects(typesym))
            {
              buf1 += sprintf(buf1, "    list_insert_front(world->null_%s,"
                              " obj);\n", typesym->name);
              buf2 += sprintf(buf2, "    list_remove(world->null_%s, obj);\n",
                              typesym->name);
            }

            buf1 += sprintf(buf1, "  }\n");
            buf2 += sprintf(buf2, "  }\n");
          }
        }
        else
        {
          buf1 += sprintf(buf1, "  }\n");
          buf2 += sprintf(buf2, "  }\n");
        }
      }
      else
      {
        /* TODO: function without arguments can refer to generated types
         * as well */
      }

      buf1 += sprintf(buf1, "}\n\n");
      buf2 += sprintf(buf2, "}\n\n");


      fprintf(fp, orig_buf1);
      fprintf(fp, orig_buf2);
    }
  }
  hashtable_itr_end(iter);

  
  free(orig_buf1);
  free(orig_buf2);
}

static void gen_logprob(genctx * ctx)
{
  FILE * fp;
  symbol * sym;
  list_t iter;

  fp = ctx->fp_genctx;

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_FUNCTION == sym->symtype) 
        && ((SYMBOL_FN_CAT_RANDOM == sym->f.category)
            || (SYMBOL_FN_CAT_NUMBER == sym->f.category)))
    {
      node * argnode;

      if (list_is_empty(sym->f.argname_list))
      {
        argnode = NULL;
      }
      else
      {
        list_item1(sym->f.argname_list, (void **)&argnode);
      }
      
      fprintf(fp, "/* probability of %s */\n", sym->name);
      fprintf(fp, "static double logprob_%s(world_t * world", sym->name);

      if (argnode)
        fprintf(fp, ", %s %s", argnode->valtype->t.gendeclname, 
                argnode->leaf.pchar);

      fprintf(fp, ")\n{\n");
      
      if (symbol_has_fixed_objects(sym->f.rettype))
      {
        if (argnode)
          fprintf(fp, "  int val = %s->%s ? (%s->%s - world->All_%s):-1;\n",
                  argnode->leaf.pchar, sym->genname, 
                  argnode->leaf.pchar, sym->genname, sym->f.rettype->name);
        else
          fprintf(fp, "  int val = world->%s ? "
                  "(world->%s - world->All_%s):-1;\n", sym->genname,
                  sym->genname, sym->f.rettype->name);
      }
      else if (symbol_has_unknown_num_objects(sym->f.rettype))
      {
        /* TODO: we are assuming that all objects are indistinguishable
         * hence putting a 0 for the index, for some distributions over
         * unknown number of objects this might not be true */
        if (argnode)
          fprintf(fp, "  int val = %s->%s ? 0 :-1;\n",
                  argnode->leaf.pchar, sym->genname);
        
        else
          fprintf(fp, "  int val = world->%s ? 0 : -1;\n", sym->genname);

      }
      else
      {
        if (argnode)
          fprintf(fp, "  %s val = %s->%s;\n", 
                  sym->f.rettype->t.gendeclname, argnode->leaf.pchar, 
                  sym->genname);
        else
          fprintf(fp, "  %s val = world->%s;\n\n",
                  sym->f.rettype->t.gendeclname, sym->genname);
      }
      
      ctx->destvar = NULL;
      ctx->desttype = sym->f.rettype;
      gen_node(ctx, sym->f.tree);
      
      fprintf(fp, "}\n\n");
    }
  }
  hashtable_itr_end(iter);
}

static void gen_sample(genctx * ctx)
{
  FILE * fp;
  symbol * sym;
  list_t iter;

  fp = ctx->fp_genctx;

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_FUNCTION == sym->symtype)
        && (SYMBOL_FN_CAT_RANDOM == sym->f.category))
    {
      node * argnode;

      if (list_is_empty(sym->f.argname_list))
      {
        argnode = NULL;
      }
      else
      {
        list_item1(sym->f.argname_list, (void **)&argnode);
      }
      
      fprintf(fp, "/* sample %s */\n", sym->name);
      fprintf(fp, "static void sample_%s(world_t * world", sym->name);

      if (argnode)
        fprintf(fp, ", %s %s", argnode->valtype->t.gendeclname, 
                argnode->leaf.pchar);
      
      fprintf(fp, ")\n{\n");

      ctx->destvar = malloc(100);

      if (symbol_has_fixed_objects(sym->f.rettype)
          || symbol_has_unknown_num_objects(sym->f.rettype))
      {
          fprintf(fp, "  int val;\n");
          sprintf(ctx->destvar, "val");
      }
      else
      { 
        if (argnode)
          sprintf(ctx->destvar, "%s->%s", argnode->leaf.pchar, sym->genname);
        else
          sprintf(ctx->destvar, "world->%s", sym->genname);
      }
      
      ctx->desttype = sym->f.rettype;
      gen_node(ctx, sym->f.tree);
      
      free(ctx->destvar);
      
      if (symbol_has_fixed_objects(sym->f.rettype)
          || symbol_has_unknown_num_objects(sym->f.rettype))
      {
        if (argnode)
          fprintf(fp, "  %s->%s", argnode->leaf.pchar, sym->genname);
        else
          fprintf(fp, "  world->%s", sym->genname);

        fprintf(fp, " = (-1 == val) ? NULL : ");

        if (symbol_has_fixed_objects(sym->f.rettype))
          fprintf(fp, "world->All_%s + val;\n", sym->f.rettype->name);

        else
          fprintf(fp, "list_index(world->All_%s, val);\n", 
                  sym->f.rettype->name);
      }
      
      fprintf(fp, "}\n\n");
    }
  }
  hashtable_itr_end(iter);
}

static char * gen_str_reprarr(symbol * typesym)
{
  if (typesym->t.repr_pointer)
    return "ptrarr";

  else if (typesym->t.repr_double)
    return "dblarr";
  
  else if (typesym->t.repr_int)
    return "intarr";
  
  else
  {
    assert(0);
    return NULL;
  }
}

static void gen_remove_block_child(genctx * ctx, symbol * fnsym, node * argnode)
{
  list_t iter;
  FILE * fp;
  
  fp = ctx->fp_genctx;  
  
  if (!fnsym->f.rettype->builtin)
    fprintf(fp, "  remove_%s(world, %s);\n", fnsym->name, 
            argnode->leaf.pchar);
  for (iter = list_next(fnsym->f.block_child); iter; 
       iter = list_next(iter))
  {
    symbol * child;
    child = (symbol *) list_item(iter);
    
    if (!child->f.rettype->builtin)
      fprintf(fp, "  remove_%s(world, %s);\n", child->name, 
              argnode->leaf.pchar);
  }
}

static void gen_sample_block_child(genctx * ctx, symbol * fnsym, node * argnode)
{
  list_t iter;
  FILE * fp;
  int blocksize;
  int i;
  
  fp = ctx->fp_genctx;  

  blocksize = 1 + list_count(fnsym->f.block_child);
  
  fprintf(fp, "    world->%s[val * %d] = %s->%s;\n",
          gen_str_reprarr(fnsym->f.rettype),
          blocksize, argnode->leaf.pchar, fnsym->genname);

  i = 1;
  for (iter = list_next(fnsym->f.block_child); iter; 
       iter = list_next(iter))
  {
    symbol * child;
    child = (symbol *) list_item(iter);
    
    fprintf(fp, "    sample_%s(world, %s);\n", child->name, 
            argnode->leaf.pchar);

    fprintf(fp, "    world->%s[val * %d + %d] = %s->%s;\n",
            gen_str_reprarr(child->f.rettype),
            blocksize, i, argnode->leaf.pchar, child->genname);

    i++;
  }
}

static void gen_add_block_child(genctx * ctx, symbol * fnsym, node * argnode)
{
  list_t iter;
  FILE * fp;
  int blocksize;
  int i;
  
  fp = ctx->fp_genctx;  

  blocksize = 1 + list_count(fnsym->f.block_child);
  
  fprintf(fp, "  %s->%s = (%s) world->%s[val * %d];\n",
          argnode->leaf.pchar, fnsym->genname, fnsym->f.rettype->t.gendeclname,
          gen_str_reprarr(fnsym->f.rettype), blocksize);

  i = 1;  
  if (!fnsym->f.rettype->builtin)
    fprintf(fp, "  add_%s(world, %s);\n", fnsym->name, 
            argnode->leaf.pchar);
  for (iter = list_next(fnsym->f.block_child); iter; 
       iter = list_next(iter))
  {
    symbol * child;
    child = (symbol *) list_item(iter);
    
    fprintf(fp, "  %s->%s = (%s) world->%s[val * %d + %d];\n",
            argnode->leaf.pchar, child->genname, 
            child->f.rettype->t.gendeclname, 
            gen_str_reprarr(child->f.rettype), blocksize, i);
    
    if (!child->f.rettype->builtin)
      fprintf(fp, "  add_%s(world, %s);\n", child->name, argnode->leaf.pchar);

    i++;
  }
}


static void gen_child_prob(genctx * ctx, symbol * fnsym, node * argnode, 
                           int skip_blockchild)
{
  list_t iter;
  FILE * fp;
  
  fp = ctx->fp_genctx;  
  
  for (iter = list_next(fnsym->f.childfns); iter;
       iter = list_next(iter))
  {
    symbol * childfn;
          
    childfn = (symbol *)list_item(iter);
          
    if (list_is_empty(childfn->f.argtype_list))
    {
      fprintf(fp, "    logwt += logprob_%s(world);\n", childfn->name);
    }
    else
    {
      symbol * childargsym;
            
      list_item1(childfn->f.argtype_list, (void **)&childargsym);
            
      if (!list_is_empty(childargsym->t.objlist))
      {
        fprintf(fp, "    for (objnum = 0; objnum < NUM_%s; objnum++)\n",
                childargsym->name);
        fprintf(fp, "      logwt += logprob_%s(world, world->All_%s + "
                "objnum);\n", childfn->name, childargsym->name);
      }
      else
      {
        fprintf(stderr, "illegal function '%s'",
                childfn->name);
        exit(1);
      }
    }
  }

  for (iter = list_next(fnsym->f.childattrs); iter;
       iter = list_next(iter))
  {
    symbol * childattr;
          
    childattr = (symbol *)list_item(iter);
    if (!skip_blockchild || !list_find(fnsym->f.block_child, childattr))
    {
      fprintf(fp, "    logwt += logprob_%s(world, %s);\n", 
              childattr->name, argnode->leaf.pchar);
    }
  }

  for (iter = list_next(fnsym->f.child_in_attrs); iter;
       iter = list_next(iter))
  {
    list_t inlist;
    symbol * infnsym;
    
    inlist = list_item(iter);
    inlist = list_next(inlist);
    
    infnsym = list_item(inlist);
    
    fprintf(fp, "    for (iter = list_next(%s->in_%s); iter; "
            "iter = list_next(iter))\n    {\n", argnode->leaf.pchar,
            infnsym->genname);

    for (inlist = list_next(inlist); inlist; inlist = list_next(inlist))
    {
      fprintf(fp, "      logwt += logprob_%s(world, list_item(iter));\n",
              ((symbol *)list_item(inlist))->name);
    }

    fprintf(fp, "    }\n");
  }
}

/* before generating the post sampler for a variable check if the only
 * valid value for that variable is none in which case don't waste time */
static void gen_check_for_null(genctx * ctx, symbol * fnsym, node * argnode)
{
  FILE * fp;
  
  fp = ctx->fp_genctx;
  
  if (symbol_has_unknown_num_objects(fnsym->f.rettype))
  {
    fprintf(fp, "  if (list_is_empty(world->All_%s))\n    return;\n\n",
            fnsym->f.rettype->name);
  }

  if (SYMBOL_FN_CAT_ORIGIN == fnsym->f.category)
    fprintf(fp, "  /* remove this %s from its originating %s */\n",
            argnode->valtype->name, fnsym->name);

  if (argnode)
    gen_remove_block_child(ctx, fnsym, argnode);

  if ((SYMBOL_FN_CAT_RANDOM == fnsym->f.category) 
      && (fnsym->f.rettype->t.genstruct))
  {
    fprintf(fp, "  /* sanity check to ensure that its not null */\n");
          
    if (!argnode)
    {
      fprintf(fp, "  sample_%s(world);\n", fnsym->name);
      fprintf(fp, "  if (!world->%s)\n    return;\n\n",
              fnsym->genname);
    }
    else
    {
      fprintf(fp, "  sample_%s(world, %s);\n", fnsym->name,
              argnode->leaf.pchar);
      fprintf(fp, "  if(!%s->%s)\n    return;\n\n",
              argnode->leaf.pchar, fnsym->genname);
    }
  }
}


static void gen_obj_child_prob(genctx * ctx, symbol * typesym)
{
  list_t iter;
  FILE * fp;

  fp = ctx->fp_genctx;

  for (iter = list_next(typesym->t.incoming_fns); iter; iter = list_next(iter))
  {
    symbol * infn;
    
    infn = list_item(iter);
    
    if (SYMBOL_FN_CAT_ORIGIN == infn->f.category)
    {
      fprintf(fp, "  log_accept += logprob_%s(world, obj);\n",
              infn->f.numfn->name);
    }
  }
}

static void gen_type_child_prob(genctx * ctx, symbol * typesym)
{
  list_t iter;
  FILE * fp;

  fp = ctx->fp_genctx;
  
  for (iter = list_next(typesym->t.childfns); iter; iter = list_next(iter))
  {
    symbol * childfn;
    symbol * childargtype;
    
    childfn = list_item(iter);
    
    fprintf(fp, "  /* account for %s children of  %s */\n", childfn->name,
            typesym->name);
    
    if (list_is_empty(childfn->f.argtype_list))
    {
      fprintf(fp, "  log_accept += logprob_%s(world);\n",
              childfn->name);
    }
    else
    {
      list_item1(childfn->f.argtype_list, (void **)&childargtype);
      fprintf(fp, "  {\n");
     
      if (symbol_has_fixed_objects(childargtype))
      {
        fprintf(fp, "    int objnum;\n");
        fprintf(fp, "    for (objnum=0; objnum < NUM_%s; objnum ++)\n    {\n",
                childargtype->name);
        fprintf(fp, "      %s childobj = world->All_%s + objnum;\n",
                childargtype->t.gendeclname, childargtype->name);
      }
      else
      {
        fprintf(fp, "    list_t iterchild;\n");
        fprintf(fp, "    for (iterchild = list_next(world->All_%s); "
                "iterchild; iterchild = list_next(iterchild))\n    {\n",
                childargtype->name);
        fprintf(fp, "      %s childobj = (%s) list_item(iterchild);\n",
                childargtype->t.gendeclname, childargtype->t.gendeclname);
      }

      fprintf(fp, "      log_accept += logprob_%s(world, childobj);\n",
              childfn->name);
      
      fprintf(fp, "    }\n");
      fprintf(fp, "  }\n");
    }
  }
}


static void gen_birth_death(genctx * ctx)
{
  hashiter_t iter;
  FILE * fp;
  symbol * sym;

  fp = ctx->fp_genctx;

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    /* generate the objects with null origin */
    if ((SYMBOL_B_TYPE == sym->symtype) && 
        symbol_has_unknown_num_objects(sym) && sym->t.null_numfn)
    {
      symbol * typesym;
      list_t iter2;

      typesym = sym;
      
      fprintf(fp, "static void birth_null_%s(world_t * world)\n{\n", 
              typesym->name);
      
      fprintf(fp, "  %s obj;\n", typesym->t.gendeclname);

      fprintf(fp, "  double log_accept;\n  list_t iter;\n\n");

      fprintf(fp, "  /* Metropolis-Hastings Move */\n\n"
              "  /* create a new object */\n");
      fprintf(fp, "  obj = %s_alloc(world);\n\n", typesym->name);

      fprintf(fp, "  log_accept = 0;\n");
      fprintf(fp, "  /* compute the probability of the previous world */\n");
      fprintf(fp, "  log_accept = +logprob_%s(world);\n\n",
              typesym->t.null_numfn->name);

      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");

      fprintf(fp, "  log_accept = -log_accept;\n");
      fprintf(fp, "  /* change the world and compute it's probability */\n");
      fprintf(fp, "  world->%s ++;\n", typesym->t.null_numfn->genname);
      fprintf(fp, "  list_insert_end(world->All_%s, obj);\n",
              typesym->name);
      fprintf(fp, "  list_insert_end(world->null_%s, obj);\n",
              typesym->name);
      fprintf(fp, "  log_accept += logprob_%s(world);\n",
              typesym->t.null_numfn->name);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_obj_child_prob(ctx, typesym);

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");

      fprintf(fp, "\n  /* if move is accepted, add the new object "
              "to the world */\n");
      
      fprintf(fp, "  if ((log_accept > 0) || (log(RANDOM_DOUBLE) < "
              "log_accept))\n");
      fprintf(fp, "    ; /* Nothing left to be done */ \n");
      
      fprintf(fp, "\n  /* otherwise, restore the old world */\n  else\n  {\n");
      fprintf(fp, "    list_delete_end(world->All_%s);\n", typesym->name);
      fprintf(fp, "    list_delete_end(world->null_%s);\n",
              typesym->name);
      fprintf(fp, "    world->%s--;\n", typesym->t.null_numfn->genname);
      fprintf(fp, "    %s_free(world, obj);\n", typesym->name);
      
      fprintf(fp, "  }\n}\n\n");
      

      fprintf(fp, "static void death_null_%s(world_t * world)\n{\n", 
              typesym->name);
      fprintf(fp, "  %s obj;\n", typesym->t.gendeclname);
      fprintf(fp, "  double log_accept;\n  list_t iter;\n\n");

      fprintf(fp, "  /* Metropolis-Hastings Move */\n\n");

      fprintf(fp, "  /* find a suitable object for killing */\n");
      fprintf(fp, "  for (iter = list_next(world->null_%s); iter; "
              "iter = list_next(iter))\n  {\n    obj = list_item(iter);\n", 
              typesym->name);
      fprintf(fp, "    if (");

      for (iter2 = list_next(typesym->t.incoming_fns); iter2; 
           iter2=list_next(iter2))
      {
        symbol * infn;
        infn = list_item(iter2);
        
        fprintf(fp, "list_is_empty(obj->in_%s) &&", infn->genname);
      }
      
      fprintf(fp, "1)\n      break;\n");

      fprintf(fp, "  }\n");
              
      fprintf(fp, "  if (!iter)\n    return;\n");
      fprintf(fp, "  list_delete_elem(iter);\n\n");

      fprintf(fp, "  log_accept = 0;\n");
              
      fprintf(fp, "  /* compute the probability of the previous world */\n");

      fprintf(fp, "  log_accept += logprob_%s(world);\n\n",
              typesym->t.null_numfn->name);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_obj_child_prob(ctx, typesym);

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");
      
      fprintf(fp, "  log_accept = -log_accept;\n\n");
      
      fprintf(fp, "  /* change the world and compute it's probability */\n");
      fprintf(fp, "  world->%s --;\n", typesym->t.null_numfn->genname);
      fprintf(fp, "  list_remove(world->All_%s, obj);\n", typesym->name);
      fprintf(fp, "  log_accept += logprob_%s(world);\n",
              typesym->t.null_numfn->name);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");
      
      fprintf(fp, "\n  /* if move is accepted, delete the object */\n");
      
      fprintf(fp, "  if ((log_accept > 0) || (log(RANDOM_DOUBLE) < "
              "log_accept))\n  {\n");
      fprintf(fp, "    %s_free(world, obj);\n", typesym->name);

      fprintf(fp, "  }\n");
      fprintf(fp, "\n  /* otherwise, restore the object */\n  else\n");
      
      fprintf(fp, "  {\n");
      
      fprintf(fp, "    world->%s ++;\n", typesym->t.null_numfn->genname);
      
      fprintf(fp, "    list_insert_end(world->null_%s, obj);\n",
              typesym->name);
      fprintf(fp, "    list_insert_end(world->All_%s, obj);\n", typesym->name);

      fprintf(fp, "  }\n");
      
      fprintf(fp, "}\n\n");
    }
    else if ((SYMBOL_B_FUNCTION == sym->symtype) && 
             (SYMBOL_FN_CAT_ORIGIN == sym->f.category))
    {
      symbol * typesym;
      list_t iter2;
      
      list_item1(sym->f.argtype_list, (void **)&typesym);

      if (!symbol_has_unknown_num_objects(typesym))
        continue;

      fprintf(fp, "static void birth_%s_%s(world_t * world, %s parobj)\n{\n",
              sym->f.rettype->name, typesym->name,
              sym->f.rettype->t.gendeclname);
      
      fprintf(fp, "  %s obj;\n", typesym->t.gendeclname);

      fprintf(fp, "  double log_accept;\n  list_t iter;\n\n");

      fprintf(fp, "  /* Metropolis-Hastings Move */\n\n"
              "  /* create a new object */\n");
      fprintf(fp, "  obj = %s_alloc(world);\n\n", typesym->name);

      fprintf(fp, "  /* compute the probability of the previous world */\n");
      fprintf(fp, "  log_accept = 0;\n");
      fprintf(fp, "  log_accept += logprob_%s(world, parobj);\n\n",
              sym->f.numfn->name);

      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");
      
      fprintf(fp, "  log_accept = -log_accept;\n");
      
      fprintf(fp, "  /* change the world and compute it's probability */\n");
      fprintf(fp, "  parobj->%s ++;\n", sym->f.numfn->genname);
      fprintf(fp, "  list_insert_end(parobj->in_%s, obj);\n", sym->genname);
      fprintf(fp, "  list_insert_end(world->All_%s, obj);\n", typesym->name);

      fprintf(fp, "  log_accept += logprob_%s(world, parobj);\n",
               sym->f.numfn->name);
      fprintf(fp, "  obj->%s = parobj;\n", sym->genname);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_obj_child_prob(ctx, typesym);

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");
      
      fprintf(fp, "\n  /* if move is accepted, add the new %s "
              "to the %s and the world */\n", 
              typesym->name, sym->f.rettype->name);
      
      fprintf(fp, "  if ((log_accept > 0) || (log(RANDOM_DOUBLE) < "
              "log_accept))\n");
      fprintf(fp, "    ; /* Nothing left to be done */ \n");

      fprintf(fp, "\n  /* otherwise, restore the old world */\n  else\n  {\n");
      fprintf(fp, "    list_delete_end(parobj->in_%s);\n", sym->genname);
      fprintf(fp, "    list_delete_end(world->All_%s);\n", typesym->name);
      fprintf(fp, "    parobj->%s --;\n", sym->f.numfn->genname);
      fprintf(fp, "    obj->%s = NULL;\n", sym->genname);
      fprintf(fp, "    %s_free(world, obj);\n  }\n", typesym->name);
      
      fprintf(fp, "}\n\n");
      
      fprintf(fp, "static void death_%s_%s(world_t * world, %s parobj)\n{\n",
              sym->f.rettype->name, typesym->name,
              sym->f.rettype->t.gendeclname);
      
      fprintf(fp, "  %s obj;\n", typesym->t.gendeclname);
      fprintf(fp, "  double log_accept;\n  list_t iter;\n\n");

      fprintf(fp, "  /* Metropolis-Hastings Move */\n\n");

      fprintf(fp, "  /* find a suitable object for killing */\n");
      fprintf(fp, "  for (iter = list_next(parobj->in_%s); iter; "
              "iter = list_next(iter))\n  {\n    obj = list_item(iter);\n", 
              sym->genname);
      fprintf(fp, "    if (");

      for (iter2 = list_next(typesym->t.incoming_fns); iter2; 
           iter2=list_next(iter2))
      {
        symbol * infn;
        infn = list_item(iter2);
        
        fprintf(fp, "list_is_empty(obj->in_%s) &&", infn->genname);
      }
      
      fprintf(fp, "1)\n      break;\n");

      fprintf(fp, "  }\n");
              
      fprintf(fp, "  if (!iter)\n    return;\n\n");

      fprintf(fp, "  /* compute the probability of the previous world */\n");

      fprintf(fp, "  log_accept = logprob_%s(world, parobj);\n\n",
              sym->f.numfn->name);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_obj_child_prob(ctx, typesym);

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");

      fprintf(fp, "  log_accept = -log_accept;\n\n");
      
      fprintf(fp, "  /* change the world and compute it's probability */\n");
      fprintf(fp, "  parobj->%s --;\n", sym->f.numfn->genname);
      fprintf(fp, "  list_delete_elem(iter);\n");
      fprintf(fp, "  list_remove(world->All_%s, obj);\n", typesym->name);
      fprintf(fp, "  log_accept += logprob_%s(world, parobj);\n\n",
              sym->f.numfn->name);
      
      fprintf(fp, "  if (!world->burnin)\n  {\n");

      gen_type_child_prob(ctx, typesym);

      fprintf(fp, "  }\n");

      fprintf(fp, "\n  /* if move is accepted, delete the object */\n");
      
      fprintf(fp, "  if ((log_accept > 0) || (log(RANDOM_DOUBLE) < "
              "log_accept))\n");
      
      fprintf(fp, "  {\n    obj->%s = NULL;\n    %s_free(world, obj);\n  }\n",
              sym->genname, typesym->name);
      
      fprintf(fp, "\n  /* otherwise, restore the object */\n  else\n");
      
      fprintf(fp, "  {\n");
      
      fprintf(fp, "    parobj->%s ++;\n", sym->f.numfn->genname);
      
      fprintf(fp, "    list_insert_end(parobj->in_%s, obj);\n", sym->genname);

      fprintf(fp, "    list_insert_end(world->All_%s, obj);\n", typesym->name);
      
      fprintf(fp, "  }\n");

      fprintf(fp, "}\n\n");
    }
  }
  hashtable_itr_end(iter);
}


static void gen_post_sample(genctx * ctx)
{
  hashiter_t iter;
  FILE * fp;
  symbol * sym;

  fp = ctx->fp_genctx;

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_FUNCTION == sym->symtype)
        && ((SYMBOL_FN_CAT_RANDOM == sym->f.category)
            || (SYMBOL_FN_CAT_ORIGIN == sym->f.category)))
    {
      nodeleaf leaf;
      node * argnode;
      symbol * fnsym;

      fnsym = sym;    

      /* get the argument of the function (if any) */
      if (SYMBOL_FN_CAT_ORIGIN == sym->f.category)
      {
        leaf.pchar = "obj";
        argnode = nodeleaf_alloc(NODETYPE_IDENTIFIER, leaf, NULL, 0);
        list_item1(fnsym->f.argtype_list, (void **)&argnode->valtype);
      }
      else if (list_is_empty(fnsym->f.argname_list))
        argnode = NULL;
      else
        list_item1(fnsym->f.argname_list, (void **)&argnode);

      fprintf(fp, "static void post_sample_%s(world_t * world", fnsym->name);

      if (argnode)
        fprintf(fp, ", %s %s", argnode->valtype->t.gendeclname, 
                argnode->leaf.pchar);
      
      fprintf(fp, ")\n");
      
      fprintf(fp, "{\n");

      /* We will do Gibbs sampling for functions whose return type has
       * fixed number of objects or unknown (but finite) number of
       * objects.
       */
      if (symbol_has_fixed_objects(fnsym->f.rettype)
          || symbol_has_unknown_num_objects(fnsym->f.rettype))
      {
        if (global->no_gibbs)
        {
          fprintf(fp, "  %s oldval = ", fnsym->f.rettype->t.gendeclname);
          if (!argnode)
            fprintf(fp, "world->%s;\n", fnsym->genname);
          else
            fprintf(fp, "%s->%s;\n", argnode->leaf.pchar,
                    fnsym->genname);
        }
        
        fprintf(fp, "  int val;\n  list_t iter;\n  RkVector_t vec;\n"
                "  double logwt;\n  int objnum;\n\n");

        gen_check_for_null(ctx, fnsym, argnode);

        if (global->no_gibbs && (SYMBOL_FN_CAT_ORIGIN != fnsym->f.category))
        {
          fprintf(fp, "  val = 0;\n");
          fprintf(fp, "  while (val < 2)\n  {\n");
          fprintf(fp, "    /* val=0 is old val; val=1 is new val */\n");
          fprintf(fp, "    if (val)\n");

          fprintf(fp, "      sample_%s(world", fnsym->name);
          if (argnode)
            fprintf(fp, ", %s", argnode->leaf.pchar);
          fprintf(fp, ");\n");
        }
        else if (symbol_has_fixed_objects(fnsym->f.rettype))
        {
          if (global->no_gibbs)
          {
            fprintf(fp, "  val = 0;\n");
            fprintf(fp, "  while (val < 2)\n  {\n");
            fprintf(fp, "    /* val=0 is old val; val=1 is new val unless "
                    "oldval is null in which case val=0 is new val*/\n");
            fprintf(fp, "    if (!oldval || val)\n");

            if (!argnode)
              fprintf(fp, "      world->%s = ", fnsym->genname);
            else
              fprintf(fp, "      %s->%s = ", argnode->leaf.pchar,
                      fnsym->genname);

            fprintf(fp, "world->All_%s + RANDOM_INDEX(NUM_%s);\n",
                    fnsym->f.rettype->name, fnsym->f.rettype->name);

          }
          else
          {
            fprintf(fp, "  val = 0;\n");
            fprintf(fp, "  while (val < NUM_%s)\n  {\n",
                    fnsym->f.rettype->name);
            fprintf(fp, "    /* change the value of %s */\n",
                    fnsym->name);

            if (!argnode)
              fprintf(fp, "    world->%s = ", fnsym->genname);
            else
              fprintf(fp, "    %s->%s = ", argnode->leaf.pchar,
                      fnsym->genname);

            fprintf(fp, "world->All_%s + val;\n",
                      fnsym->f.rettype->name);
          }
        }
        else
        {
          if (global->no_gibbs)
          {
            fprintf(fp, "  val = 0;\n");
            fprintf(fp, "  while (val < 2)\n  {\n");
            fprintf(fp, "    /* val=0 is old val; val=1 is new val unless "
                    "oldval is null in which case val=0 is new val*/\n");
            fprintf(fp, "    if (!oldval || val)\n");
            if (!argnode)
              fprintf(fp, "      world->%s = ", fnsym->genname);
            else
              fprintf(fp, "      %s->%s = ", argnode->leaf.pchar,
                      fnsym->genname);

            fprintf(fp, "(%s) list_index(world->All_%s, "
                    "RANDOM_INDEX(list_count(world->All_%s)));\n",
                    fnsym->f.rettype->t.gendeclname, fnsym->f.rettype->name,
                    fnsym->f.rettype->name);
            
          }
          else
          {
            fprintf(fp, "  val = 0;\n");
            fprintf(fp, "  for (iter = list_next(world->All_%s);"
                    " iter; iter = list_next(iter))\n  {\n",
                    fnsym->f.rettype->name);
            fprintf(fp, "    /* change the value of %s */\n",
                    fnsym->name);

            if (!argnode)
              fprintf(fp, "    world->%s = ", fnsym->genname);
            else
              fprintf(fp, "    %s->%s = ", argnode->leaf.pchar,
                      fnsym->genname);

            fprintf(fp, "(%s) list_item(iter);\n",
                    fnsym->f.rettype->t.gendeclname);
          }
        }
        
           
        fprintf(fp, "    /* compute the probability of this value */\n");
        
        if (SYMBOL_FN_CAT_ORIGIN == fnsym->f.category)
        {
          fprintf(fp, "    %s->%s->%s ++;\n",
                  argnode->leaf.pchar, fnsym->genname, 
                  fnsym->f.numfn->genname);
          fprintf(fp, "    logwt = logprob_%s(world, %s->%s);\n",
                  fnsym->f.numfn->name, argnode->leaf.pchar, fnsym->genname);

          if (!symbol_has_fixed_objects(argnode->valtype))
          {
            fprintf(fp, "    logwt += log(%s->%s->%s); "
                    "/* account for indistinguishability */\n",
                    argnode->leaf.pchar, fnsym->genname, 
                    fnsym->f.numfn->genname);
          }
        }
        else
        {
          if (fnsym->f.cpdtype.uncond_uniform)
          {
            fprintf(fp, "    logwt = 0;  /* uniform prior */\n");
          }
          else if (!argnode)
          {
            fprintf(fp, "    logwt = logprob_%s(world);\n", fnsym->name);
          }
          else
          {
            fprintf(fp, "    logwt = logprob_%s(world, %s);\n", fnsym->name,
                    argnode->leaf.pchar);
          }
        }

        fprintf(fp, "    /* now, compute the probability of the children */\n"
          );

        /* block sample the children in the block */
        if (argnode)
          gen_sample_block_child(ctx, fnsym, argnode);

        gen_child_prob(ctx, fnsym, argnode, 1);
        
        if (SYMBOL_FN_CAT_ORIGIN == fnsym->f.category)
        {
          fprintf(fp, "    %s->%s->%s --;\n",
                  argnode->leaf.pchar, fnsym->genname, 
                  fnsym->f.numfn->genname);
          fprintf(fp, "    logwt -= logprob_%s(world, %s->%s);\n",
                  fnsym->f.numfn->name, argnode->leaf.pchar, fnsym->genname);
        }

        fprintf(fp, "    world->wtarr[val] = logwt;\n");
        fprintf(fp, "    val ++;\n");

        /* if this function can be null (due to a null origin statement)
         * then we need to try the null value with 50% probability */
        if (global->no_gibbs && (SYMBOL_FN_CAT_ORIGIN == fnsym->f.category)
            && (argnode->valtype->t.null_numfn))
          fprintf(fp, "    if (!oldval || (RANDOM_DOUBLE < .5))\n"
                  "      break;\n");

        fprintf(fp, "  }\n\n");

        if ((SYMBOL_FN_CAT_ORIGIN == fnsym->f.category)
            && (argnode->valtype->t.null_numfn))
        {
          fprintf(fp, "  /* also try for it to be null */\n");
          
          if (global->no_gibbs)
            fprintf(fp, "  if (1 == val)\n");
          
          fprintf(fp, "  {\n    %s->%s = NULL;\n",
                  argnode->leaf.pchar, fnsym->genname);
          fprintf(fp, "    world->%s ++;\n",
                  argnode->valtype->t.null_numfn->genname);
          fprintf(fp, "    logwt = logprob_%s(world);\n",
                  argnode->valtype->t.null_numfn->name);

          gen_sample_block_child(ctx, fnsym, argnode);
          gen_child_prob(ctx, fnsym, argnode, 1);

          fprintf(fp, "    world->%s --;\n",
                  argnode->valtype->t.null_numfn->genname);
          fprintf(fp, "    logwt -= logprob_%s(world);\n",
                  argnode->valtype->t.null_numfn->name);

          fprintf(fp, "    world->wtarr[val] = logwt;\n");
          fprintf(fp, "    val ++;\n  }\n\n");
        }
        
        fprintf(fp, "  RkVector_init(&vec, val, world->wtarr);\n");
        fprintf(fp, "  RkVector_lognormalize(&vec);\n");
        fprintf(fp, "  val = RkVector_sample(&vec);\n");

        if (argnode)
          gen_add_block_child(ctx, fnsym, argnode);

        else
          fprintf(fp, "  world->%s = world->All_%s + val;\n",
                  fnsym->genname, fnsym->f.rettype->name);
      }
      
      else if (fnsym->f.rettype->t.infinite || global->no_gibbs)
      {
        fprintf(fp, "  %s old_val;\n  int val;\n  double logwt;\n"
                "  list_t iter;\n\n", fnsym->f.rettype->t.gendeclname);

        if (symbol_has_unknown_num_objects(fnsym->f.rettype))
        {
          fprintf(fp, "  if (list_is_empty(world->All_%s))\n    return;\n\n",
                  fnsym->f.rettype->name);
        }

        /* check if it is always null */
        if ((SYMBOL_FN_CAT_RANDOM == fnsym->f.category) 
            && (fnsym->f.rettype->t.genstruct))
        {
          fprintf(fp, "  /* sanity check to ensure that its not null */\n");
          
          if (!argnode)
          {
            fprintf(fp, "  if (!world->%s)\n  {\n",
                    fnsym->genname);
            fprintf(fp, "    sample_%s(world);\n", fnsym->name);

          }
          else
          {
            fprintf(fp, "  if(!%s->%s)\n  {\n",
                    argnode->leaf.pchar, fnsym->genname);
            fprintf(fp, "    sample_%s(world, %s);\n", fnsym->name,
                    argnode->leaf.pchar);
            
            if (!fnsym->f.rettype->builtin)
              fprintf(fp, "    add_%s(world, %s);\n", fnsym->name, 
                      argnode->leaf.pchar);
          }
          fprintf(fp, "    return;\n  }\n");
        }
        
        fprintf(fp, "  /* Metropolis-Hastings Move */\n  old_val = ");

        /* save the old value */
        if (argnode)
          fprintf(fp, "%s->%s;\n", argnode->leaf.pchar, fnsym->genname);
        else
          fprintf(fp, "world->%s;\n", fnsym->genname);

        if (argnode && !fnsym->f.rettype->builtin)
          fprintf(fp, "  remove_%s(world, %s);\n", fnsym->name, 
                  argnode->leaf.pchar);

        fprintf(fp, "  logwt = 0;\n");
        fprintf(fp, "  for (val=0; val<2; val ++)\n  {\n");
        fprintf(fp, "    if (val)\n    {\n");
        fprintf(fp, "      logwt = -logwt;\n");

        fprintf(fp, "      sample_%s(world", fnsym->name);
        if (argnode)
          fprintf(fp, ", %s", argnode->leaf.pchar);
        fprintf(fp, ");\n");

        fprintf(fp, "    }\n");
        
        gen_child_prob(ctx, fnsym, argnode, 0);

        fprintf(fp, "  }\n  /* if move is rejected restore old value */\n");
        fprintf(fp, "  if ((logwt < 0) && (log(RANDOM_DOUBLE) > logwt))\n");

        if (argnode)
          fprintf(fp, "    %s->%s", argnode->leaf.pchar, fnsym->genname);
        else
          fprintf(fp, "    world->%s", fnsym->genname);

        fprintf(fp, " = old_val;\n");
        
        if (argnode && !fnsym->f.rettype->builtin)
          fprintf(fp, "  add_%s(world, %s);\n", fnsym->name, 
                  argnode->leaf.pchar);
      }
      
      fprintf(fp, "}\n\n");
    }
  }
  hashtable_itr_end(iter);
  
  fprintf(fp, "static void post_sample(world_t * world)\n{\n"
          "  list_t iter;\n  int objnum;\n\n");

  fprintf(fp, "  /* birth and death moves */\n\n");

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_TYPE == sym->symtype) 
        && symbol_has_unknown_num_objects(sym) && sym->t.null_numfn)
    {
      fprintf(fp, "  /* null -> %s */\n", sym->name);
      fprintf(fp, "  if (RANDOM_DOUBLE < .5)\n"
              "    birth_null_%s(world);\n", sym->name);
      fprintf(fp, "  else\n"
              "    death_null_%s(world);\n", sym->name);
    }
    else if ((SYMBOL_B_FUNCTION == sym->symtype) && 
             (SYMBOL_FN_CAT_ORIGIN == sym->f.category))
    {
      symbol * typesym;
      
      list_item1(sym->f.argtype_list, (void **)&typesym);

      if (!symbol_has_unknown_num_objects(typesym))
        continue;

      fprintf(fp, "  /* %s -> %s */\n", sym->f.rettype->name, typesym->name);
      
      if (symbol_has_fixed_objects(sym->f.rettype))
      {
        fprintf(fp, "  for (objnum=0; objnum < NUM_%s; objnum++)\n",
                sym->f.rettype->name);
        fprintf(fp, "    if (RANDOM_DOUBLE < .5)\n"
                "      birth_%s_%s(world, world->All_%s + objnum);\n",
                sym->f.rettype->name, typesym->name, sym->f.rettype->name);
        fprintf(fp, "    else\n"
                "      death_%s_%s(world, world->All_%s + objnum);\n",
                sym->f.rettype->name, typesym->name, sym->f.rettype->name);
      }
      else if (symbol_has_unknown_num_objects(sym->f.rettype))
      {
        fprintf(fp, "  for (iter = list_next(world->All_%s); iter; "
                "iter = list_next(iter))\n", sym->f.rettype->name);
        fprintf(fp, "    if (RANDOM_DOUBLE < .5)\n"
                "      birth_%s_%s(world, (%s) list_item(iter));\n",
                sym->f.rettype->name, typesym->name, 
                sym->f.rettype->t.gendeclname);
        fprintf(fp, "    else\n"
                "      death_%s_%s(world, (%s) list_item(iter));\n",
                sym->f.rettype->name, typesym->name, 
                sym->f.rettype->t.gendeclname);
      }
    }
  }
  hashtable_itr_end(iter);

  fprintf(fp, "\n  /* sample functions */\n\n");

  for(iter = symbol_table_itr_start();
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    if ((SYMBOL_B_FUNCTION == sym->symtype)
        && ((SYMBOL_FN_CAT_RANDOM == sym->f.category)
            ||(SYMBOL_FN_CAT_ORIGIN == sym->f.category)))
    {
      symbol * fnsym;
      fnsym = sym;
      
      fprintf(fp, "  /* %s */\n", fnsym->name);
      
      if (list_is_empty(fnsym->f.argtype_list))
      {
        if (!fnsym->f.zary_obs)
          fprintf(fp, "  post_sample_%s(world);\n", fnsym->name);
      }
      else
      {
        symbol * childargsym;
            
        list_item1(fnsym->f.argtype_list, (void **)&childargsym);
            
        if (symbol_has_fixed_objects(childargsym))
        {
          list_t iter3;
          
          for (iter3 = list_next(childargsym->t.objlist); iter3; 
               iter3 = list_next(iter3))
          {
            symbol * obj;
            obj = (symbol *)list_item(iter3);
            list_t iter4;

            for (iter4 = list_next(obj->o.obs_attr_vals); iter4;
                 iter4 = list_next(iter4))
            {
              symbol * obssym;
              node * obsval;
        
              list_item2(list_item(iter4), (void **)&obssym,
                         (void **)&obsval);

              if (obssym == fnsym)
                break;
            }
            
            if (!iter4)
              fprintf(fp, "  post_sample_%s(world, world->All_%s + %d);\n", 
                      fnsym->name, childargsym->name, obj->o.objnum);
          }
        }
        else
        {
          fprintf(fp, "  for (iter = list_next(world->All_%s); iter;"
                  " iter = list_next(iter))\n", childargsym->name);
          fprintf(fp, "    post_sample_%s(world, (%s *)list_item(iter));\n",
                  fnsym->name, childargsym->genname);
        }
      }
    }
  }
  hashtable_itr_end(iter);
  
  fprintf(fp, "}\n\n");
}

static void gen_query(genctx * ctx)
{
  list_t iter;
  FILE * fp;
  
  fp = ctx->fp_genctx;
  fprintf(fp, "static void query_update(world_t *world)\n");
  fprintf(fp, "{\n");
  
  for (iter = list_next(ctx->qrylist_genctx); iter; iter = list_next(iter))
  {
    genqry * qry;
    list_t nullconds;

    qry = (genqry *)list_item(iter);

    nullconds = list_alloc();
    
    gen_null_conds(ctx, qry->tree, nullconds);
    
    if (!list_is_empty(nullconds))
    {
      list_t iter2;
      
      fprintf(fp, "  if (");

      for (iter2 = list_next(nullconds); iter2; iter2 = list_next(iter2))
      {
        char * cond;

        cond = (char *)list_item(iter2);
        
        fprintf(fp, "!%s", cond);

        free(cond);
        
        if (list_next(iter2))
          fprintf(fp, " || ");
      }

      fprintf(fp, ")\n    Report_%s_Update_Null(&world->query%d);\n"
              "  else\n  ", qry->reptype, qry->qnum);
    }
    
    list_free(nullconds);
    
    fprintf(fp, "  Report_%s_Update(&world->query%d, ",
            qry->reptype, qry->qnum);
    if (qry->tree->valtype->t.has_guarobjs)
    {
      fprintf(fp, "(int) (");
      gen_node(ctx, qry->tree);
      fprintf(fp, " - world->All_%s)", qry->tree->valtype->name);
    }
    else
      gen_node(ctx, qry->tree);

    fprintf(fp, ");\n\n");
  }
  
  fprintf(fp, "}\n\n");
  
  fprintf(fp, "static void query_print(world_t *world)\n");
  fprintf(fp, "{\n");
  for (iter = list_next(ctx->qrylist_genctx); iter; iter = list_next(iter))
  {
    genqry * qry;
    
    qry = list_item(iter);
    fprintf(fp, "  printf(\" Query%d: \");\n", qry->qnum);
    fprintf(fp, "  Report_%s_Print(&world->query%d);\n",
            qry->reptype, qry->qnum);
  }
  fprintf(fp, "}\n\n");

  fprintf(fp, "static void query_print_last(world_t *world)\n");
  fprintf(fp, "{\n");
  for (iter = list_next(ctx->qrylist_genctx); iter; iter = list_next(iter))
  {
    genqry * qry;
    
    qry = list_item(iter);
    fprintf(fp, "  printf(\" Query%d: \");\n", qry->qnum);
    fprintf(fp, "  Report_%s_Print_Last(&world->query%d);\n",
            qry->reptype, qry->qnum);
  }
  fprintf(fp, "}\n\n");
}

static void gen_main(genctx * ctx)
{
  FILE * fp;

  fp = ctx->fp_genctx;
  
  fprintf(fp, "int main(int argc, char ** argv)\n");
  fprintf(fp, "{\n");
  fprintf(fp, "  time_t t1, t2;\n");
  fprintf(fp, "  int i;\n");
  fprintf(fp, "  options * opt;\n");
  fprintf(fp, "  world_t * world;\n");
  fprintf(fp, "\n");
  fprintf(fp, "  printf(\"" PACKAGE " " VERSION " -- BLOG Inference\\n\");\n");
  fprintf(fp, "\n");
  fprintf(fp, "  opt = options_parse(argc, argv);\n\n");
  fprintf(fp, "  if (opt->randomize_opt)\n    srand(time(NULL));\n");
  fprintf(fp, "  else\n    srand(123456789);\n\n");
  fprintf(fp, "  world = world_alloc();\n\n");
  fprintf(fp, "  /* generate samples and update query counts */\n");
  fprintf(fp, "  t1 = time(NULL);\n");
  fprintf(fp, "  for (i=0; i<opt->num_samples_opt; i++)\n");
  fprintf(fp, "  {\n");
  fprintf(fp, "    if (i < opt->burn_in_opt)\n      world->burnin = 1;\n"
          "    else\n      world->burnin = 0;\n\n");
  fprintf(fp, "    /* sample all the variables from their posterior */\n");
  fprintf(fp, "    post_sample(world);\n\n");
  fprintf(fp, "    if (i >= opt->burn_in_opt)\n");
  fprintf(fp, "      query_update(world);  /* update the query counts */\n\n");
  fprintf(fp, "    if ((0 == ((i+1) %% opt->interval_opt))\n");
  fprintf(fp, "        || ((i+1) == opt->num_samples_opt))\n");
  fprintf(fp, "    {\n");
  fprintf(fp, "      t2 = time(NULL);\n");
  fprintf(fp, "      printf(\"Samples done: %%d. Elapsed %%d s.\\n\", i+1, "
          "t2-t1);\n");
  fprintf(fp, "      if (opt->lastval_opt)\n");
  fprintf(fp, "        query_print_last(world); /* print the last world */\n");
  fprintf(fp, "      else\n");
  fprintf(fp, "        query_print(world); /* report results */\n");
  fprintf(fp, "    }\n");
  fprintf(fp, "  }\n\n");
  fprintf(fp, "  return 0;\n");
  fprintf(fp, "}\n");
}

/* converts a node to C code which represents the node's value */
static char * gen_node_str(genctx * ctx, char * buf, node * node1)
{
  node * node2;
  node * node3;
  node * node4;
  node * node5;
  
  list_t iter;

  switch(node1->type)
  {
  case NODETYPE_NULL:
    buf += sprintf(buf, "NULL");
    break;
    
  case NODETYPE_INTEGER:
    buf += sprintf(buf, "%d", node1->leaf.intval);
    break;
    
  case NODETYPE_REAL:
    buf += sprintf(buf, "%f", node1->leaf.doubleval);
    break;
    
  case NODETYPE_STRING:
    buf += sprintf(buf, node1->leaf.pchar);
    break;

  case NODETYPE_IDENTIFIER:
    buf += sprintf(buf, node1->leaf.pchar);
    break;
    
  case NODETYPE_SYMBOL:
    if (SYMBOL_B_OBJECT == node1->leaf.psym->symtype)
    {
      if (node1->leaf.psym->o.objtype->t.genstruct)
        buf += sprintf(buf, "(world->All_%s+%d)", 
                       node1->leaf.psym->o.objtype->name,
                       node1->leaf.psym->o.objnum);
      else
        buf += sprintf(buf, node1->leaf.psym->genname);
    }
    else if (SYMBOL_B_FUNCTION == node1->leaf.psym->symtype)
      buf += sprintf(buf, "world->%s", node1->leaf.psym->genname);

    else
      node_error(node1, "unexpected symbol '%s'", node1->leaf.psym->name);
    break;
  
  case NODETYPE_RkVECTOR:
    buf += sprintf(buf, "RkVector_alloc(%d, ", list_count(node1->childlist));
    for (iter = list_next(node1->childlist); iter; iter = list_next(iter))
    {
      buf = gen_node_str(ctx, buf, (node *)list_item(iter));
      if (list_next(iter))
        buf += sprintf(buf, ", ");
    }
    buf += sprintf(buf, ")");
    break;

  case NODETYPE_FN_CALL:
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);
    assert(1 == list_count(node3->childlist));
    buf = gen_node_str(ctx, buf, 
                       (node *)list_item(list_next(node3->childlist)));
    assert(NODETYPE_SYMBOL == node2->type);
    buf += sprintf(buf, "->%s", node2->leaf.psym->genname);
    break;
    
  case NODETYPE_AND:
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);

    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node2);
    if (!strcmp(node2->valtype->name, "Boolean"))
      buf += sprintf(buf, " == world->All_Boolean");
    buf += sprintf(buf, ")");

    buf += sprintf(buf, " && ");

    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node3);
    if (!strcmp(node3->valtype->name, "Boolean"))
      buf += sprintf(buf, " == world->All_Boolean");
    buf += sprintf(buf, ")");
    break;

  case NODETYPE_OR:
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);

    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node2);
    if (!strcmp(node2->valtype->name, "Boolean"))
      buf += sprintf(buf, " == world->All_Boolean");
    buf += sprintf(buf, ")");

    buf += sprintf(buf, " || ");

    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node3);
    if (!strcmp(node3->valtype->name, "Boolean"))
      buf += sprintf(buf, " == world->All_Boolean");
    buf += sprintf(buf, ")");
    break;

  case NODETYPE_EQUAL:
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);
    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node2);
    buf += sprintf(buf, ")");
    buf += sprintf(buf, " == ");
    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node3);
    buf += sprintf(buf, ")");
    break;
    
  case NODETYPE_NOT_EQUAL:
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);
    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node2);
    buf += sprintf(buf, ")");
    buf += sprintf(buf, " != ");
    buf += sprintf(buf, "(");
    buf = gen_node_str(ctx, buf, node3);
    buf += sprintf(buf, ")");
    break;

  case NODETYPE_CPD_CALL:
    node_error(node1, "illegal expression");
    break;

  case NODETYPE_IF:
    if (2 == list_count(node1->childlist))
    {
      list_item2(node1->childlist, (void **)&node2, (void **)&node3);
      node4 = NULL;
    }
    else
      list_item3(node1->childlist, (void **)&node2, (void **)&node3,
                 (void **)&node4);
    buf += sprintf(buf, "  if (");

    buf = gen_node_str(ctx, buf, node2);

    if (!strcmp(node2->valtype->name, "Boolean"))
      buf += sprintf(buf, " == world->All_Boolean");
    
    buf += sprintf(buf, ")\n  {\n");
    buf = gen_node_str(ctx, buf, node3);
    buf += sprintf(buf, "  }\n");
    if (node4)
    {
      buf += sprintf(buf, "  else\n  {\n");
      buf = gen_node_str(ctx, buf, node4);
      buf += sprintf(buf, "  }\n");
    }
    else
    {
      if (ctx->desttype->t.has_guarobjs)
      {
        if (ctx->destvar)
          buf += sprintf(buf, "  else\n  {\n    %s = -1;\n  }\n",
                         ctx->destvar);
        else
          buf += sprintf(buf, "  else\n  {\n    if (-1 == val)\n"
                         "     return 1;\n"
                         "    else\n      return 0;\n  }\n");
      }
      else if (ctx->desttype->t.genstruct)
      {
        if (ctx->destvar)
          buf += sprintf(buf, "  else\n  {\n    %s = NULL;\n  }\n",
                         ctx->destvar);
        else
          buf += sprintf(buf, "  else\n  {\n    if (!val)\n     return 1;\n"
                         "    else\n      return 0;\n  }\n");
      }
      else
        buf += sprintf(buf, "  else\n  {\n    "
                       "fprintf(stderr, \"abort: missing else\\n\");\n    "
                       "exit(1);\n  }\n");
    }
    
    break;

  case NODETYPE_DET_DEP:
    node_error(node1, "illegal expression");
    break;

  case NODETYPE_STO_DEP:
    list_item1(node1->childlist, (void **)&node2);
    assert(NODETYPE_CPD_CALL == node2->type);
    list_item3(node2->childlist, (void **)&node3, (void **)&node4, 
               (void **)&node5);

    if (ctx->destvar)
    {
      buf += sprintf(buf, "  %s = ",
                     ctx->destvar);

      buf += sprintf(buf, "%s_Sample(&world->%s, %d",
                     node3->leaf.pchar, node2->genname, 
                     list_count(node5->childlist));
    }
    else
    { 
      buf += sprintf(buf, "  return %s_LogProb(&world->%s, val", 
                     node3->leaf.pchar, node2->genname);

      buf += sprintf(buf, ", %d", list_count(node5->childlist));
    }

    for (iter = list_next(node5->childlist); iter; iter = list_next(iter))
    {
      node * argnode;
      
      argnode = (node *)list_item(iter);
      buf += sprintf(buf, ", ");
      if (argnode->valtype && argnode->valtype->t.has_guarobjs)
      {
        buf += sprintf(buf, " (int) (");
        buf = gen_node_str(ctx, buf, argnode);
        buf += sprintf(buf, " - world->All_%s)", argnode->valtype->name);
      }
      else
        buf = gen_node_str(ctx, buf, argnode); 
    }
    
    buf += sprintf(buf, ");\n");
    break;

  case NODETYPE_SET_IMP:
    if (1 != list_count(node1->childlist))
      node_error(node1, "illegal condition in set expression");
    
    list_item1(node1->childlist, (void **)&node2);

    assert(NODETYPE_IDENTIFIER == node2->type);

    if (symbol_has_fixed_objects(node2->valtype))
      buf += sprintf(buf, "NUM_%s", node2->valtype->name);

    else if (symbol_has_unknown_num_objects(node2->valtype))
      buf += sprintf(buf, "list_count(world->All_%s)", node2->valtype->name);

    else
      node_error(node2, "illegal set of '%s'", node2->valtype->name);

    break;
    
  case NODETYPE_SET_EXP:
    node_error(node1, "illegal expression");
    break;
    
  case NODETYPE_SET_CARD:
    list_item1(node1->childlist, (void **)&node2);
    
    if (NODETYPE_SET_EXP == node2->type)
    {
      buf += sprintf(buf, "%d", list_count(node2->childlist));
    }
    else
    {
      assert(NODETYPE_SET_IMP == node2->type);

      if (1 != list_count(node2->childlist))
        node_error(node2, "illegal condition in set expression");

      list_item1(node2->childlist, (void **)&node3);
    
      assert(NODETYPE_IDENTIFIER == node3->type);
    
      if (symbol_has_fixed_objects(node3->valtype))
        buf += sprintf(buf, "NUM_%s", node3->valtype->name);

      else if (symbol_has_unknown_num_objects(node3->valtype))
        buf += sprintf(buf, "list_count(world->All_%s)", node3->valtype->name);

      else
        node_error(node3, "illegal set of '%s'", node3->valtype->name);
    }
    break;
    
  default:
    node_error(node1, "unknown expression");
    break;
  }

  return buf;
}

static void gen_node(genctx * ctx, node * node1)
{
  char buf[10000];

  buf[0] = '\0';
  gen_node_str(ctx, buf, node1);
  fprintf(ctx->fp_genctx, buf);
}

/* generates conditions under which the node can be null */
static void gen_null_conds(genctx * ctx, node * node1, list_t nullconds)
{
  list_t iter;

  if (node1->childlist)
  {
    for (iter = list_next(node1->childlist); iter; iter = list_next(iter))
    {
      gen_null_conds(ctx, (node *)list_item(iter), nullconds);
    }
  }
  
  if (node1->valtype && !node1->valtype->builtin 
      && node1->valtype->t.genstruct)
  {
    if (((NODETYPE_SYMBOL == node1->type) &&
        (SYMBOL_B_FUNCTION == node1->leaf.psym->symtype))
        || (NODETYPE_FN_CALL == node1->type))
    {
      char buf[10000];
      
      gen_node_str(ctx, buf, node1);
      
      list_insert_end(nullconds, gen_strdup(buf));
    }
  }
}

/* my copy of strdup since strdup is not in ANSI C '99 */
char * gen_strdup(const char * src)
{
  char * tgt;
  int len;

  len = strlen(src);

  tgt = (char *) malloc((len + 1) * sizeof(*tgt));
  
  strcpy(tgt, src);
  
  return tgt;
}
